import java.util.Scanner;

/**
 * Created with IntelliJ IDEA
 * Description Java实现无符号数识别的有穷自动机（DFA）
 * User: 王杰
 * Date: 2025-05-10
 * Time: 8:44
 * 无符号数的定义：
 * 1. 整数部分：可有可无，由数字组成
 * 2. 小数部分：可有可无，由小数点后跟数字组成
 * 3. 指数部分：可有可无，由e/E后跟指数组成，指数可以有正负号
 * 示例：
 * 合法：123, 123.456, 123e10, 123.456E-10
 * 非法：123., 123e, 123.456e+10.
 */

public class UnsignedNumberRecognizer {
    // 状态定义
    private static final int S0 = 0;   // 初始状态：尚未读取任何字符
    private static final int S1 = 1;   // 已接收数字，可以是整数部分或小数部分的数字
    private static final int S2 = 2;   // 已接收小数点，但小数点后必须有数字
    private static final int S3 = 3;   // 已接收e/E，表示指数部分开始，但指数部分必须有数字
    private static final int S4 = 4;   // 已接收指数符号（+/-），但符号后必须有数字
    private static final int S5 = 5;   // 完整指数数字，指数部分已完整
    private static final int S6 = 6;   // 错误状态：无法识别的字符或不符合规则的输入

    // 输入字符分类
    private static final int DIGIT = 0;    // 数字字符
    private static final int DOT = 1;      // 小数点字符
    private static final int EXP = 2;      // 指数字符e/E
    private static final int SIGN = 3;     // 符号字符+/-（仅在指数部分有效）
    private static final int OTHER = 4;    // 其他无效字符

    // 状态转移表
    // 每一行代表一个状态，每一列代表一个输入字符类别
    // 表中的值表示在当前状态和输入字符类别下，自动机应转移到的新状态
    private static final int[][] TRANS_TABLE = {
            /* S0 */ {S1, S2, S6, S6, S6},  // 初始状态，只能以数字或.开头
            /* S1 */ {S1, S2, S3, S6, S6},  // 数字后接数字、.或e
            /* S2 */ {S5, S6, S3, S6, S6},  // .后必须接数字
            /* S3 */ {S5, S6, S6, S4, S6},  // e后接数字或+/-（指数部分开始）
            /* S4 */ {S5, S6, S6, S6, S6},  // +/-后必须接数字
            /* S5 */ {S5, S6, S3, S6, S6},  // 指数数字可继续或接e
            /* S6 */ {S6, S6, S6, S6, S6}   // 错误吸收状态，无法再转移
    };

    /**
     * 对输入字符进行分类
     * @param c 输入字符
     * @return 字符类别，可能是DIGIT、DOT、EXP、SIGN或OTHER
     */
    private static int classifyChar(char c) {
        if (Character.isDigit(c)) return DIGIT;  // 如果是数字字符
        if (c == '.') return DOT;                // 如果是小数点字符
        if (c == 'e' || c == 'E') return EXP;    // 如果是指数字符e/E
        if (c == '+' || c == '-') return SIGN;   // 如果是符号字符+/-（仅在指数部分有效）
        return OTHER;                            // 其他无效字符
    }

    /**
     * 判断输入字符串是否为合法的无符号数
     * @param input 输入字符串
     * @return 如果输入字符串是合法的无符号数，返回true；否则返回false
     */
    public static boolean isUnsignedNumber(String input) {
        int state = S0;  // 初始状态
        int errorPos = -1;  // 错误位置，初始化为-1表示尚未发现错误

        // 遍历输入字符串的每个字符
        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);  // 获取当前字符
            int inputType = classifyChar(c);  // 对当前字符进行分类
            state = TRANS_TABLE[state][inputType];  // 根据当前状态和输入字符类别，更新状态

            // 如果进入错误状态，记录错误位置并退出循环
            if (state == S6) {
                errorPos = i;
                break;
            }
        }

        // 如果发现错误，输出错误信息
        if (errorPos != -1) {
            System.out.printf("错误：位置 %d 的字符 '%c' 不合法%n",
                    errorPos + 1, input.charAt(errorPos));
            return false;
        }

        // 强化最终状态检查
        // 只有在S1、S5或S2（且以.结尾且长度大于1）时，才认为是合法的无符号数
        boolean valid = (state == S1 ||  // 纯整数
                state == S5 ||  // 小数或完整指数
                (state == S2 && input.endsWith(".") && input.length() > 1)); // 如"123."

        // 如果不合法，输出错误信息
        if (!valid) {
            System.out.println("错误：不完整的数字格式");
        }
        return valid;
    }

    /**
     * 主函数，用于测试无符号数识别器
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);  // 创建扫描器，用于读取用户输入
        System.out.println("无符号数识别器（输入quit退出）");

        // 循环读取用户输入并进行判断
        while (true) {
            System.out.print("请输入测试字符串: ");
            String input = scanner.nextLine().trim();  // 读取用户输入并去除首尾空格

            // 如果用户输入"quit"，退出程序
            if (input.equalsIgnoreCase("quit")) {
                break;
            }

            // 调用isUnsignedNumber方法判断输入字符串是否为合法的无符号数
            if (isUnsignedNumber(input)) {
                System.out.println("✔ 合法的无符号数");
            } else {
                System.out.println("✘ 非法的无符号数");
            }
            System.out.println();
        }

        scanner.close();  // 关闭扫描器
    }
}